package v1

import (
	"context"
	"encoding/json"
	"fmt"

	"wxpay/internal/conf"
	"wxpay/internal/dao"
	"wxpay/internal/model"
	"wxpay/internal/service"
	"wxpay/pkg/merrors"

	"github.com/go-redis/redis/v8"

	"runtime"

	"wxpay/pkg/mcontext"
	"wxpay/pkg/notify"

	"github.com/nsqio/go-nsq"
)

func failOver() {
	if r := recover(); r != nil {
		buf := make([]byte, 64<<10)
		buf = buf[:runtime.Stack(buf, false)]
		errInfo := fmt.Sprintf("worker: panic recovered: %s\n%s", r, buf)
		ctx := context.Background()
		log := mcontext.Logger(ctx)
		log.Error(errInfo)
	}
}

type WorkerI interface {
	Do(context.Context, *nsq.Message) error
	Name() string
}

func getWorker(topic string, ds dao.DataStore, r *redis.Client) (WorkerI, error) {
	const op merrors.Op = "getWorker"
	switch topic {
	case model.TopicMessageRetry:
		return MessageRetryHandler{ds}, nil
	case model.TopicWithdrawAction:
		return SubMchWithdrawHandler{ds}, nil
	case model.TopicWithdrawQuery:
		return SubMchWithdrawQueryHandler{ds}, nil
	case model.TopicTransferAction:
		return BatchTransferHandler{ds, r}, nil
	case model.TopicApplymentQuery:
		return EcommerceMchApplymentQueryHandler{ds}, nil
	case model.TopicTradeQuery:
		return TradeQueryHandler{ds}, nil
	case model.TopicRefundQuery:
		return RefundQueryHandler{ds}, nil
	case model.TopicProfitOrderQuery:
		return ProfitOrderQueryHandler{ds}, nil
	case model.TopicProfitReturnQuery:
		return ProfitReturnQueryHandler{ds}, nil
	case model.TopicTransferQuery:
		return BatchTransferQueryHandler{ds, r}, nil
	case model.TopicTransferNotify:
		return TransferDetailsCompensateHandler{ds, r}, nil
	case model.TopicAliFaceToFaceSignQuery:
		return SubMchAliFaceToFaceSignQueryHandler{ds}, nil
	case model.TopicSubMchApplymentQuery:
		return SubMchApplymentQueryHandler{ds}, nil
	}
	return nil, merrors.E(op, merrors.Internal, merrors.ErrInternal, fmt.Sprintf("找不到tpoic:%s 对应的worker", topic))
}

type Consumer struct {
	worker WorkerI
}

func (c Consumer) HandleMessage(m *nsq.Message) (err error) {
	defer failOver()
	ctx := mcontext.GenContextWithRqId()
	log := mcontext.Logger(ctx)
	log.Info(fmt.Sprintf("%s收到消息%s", c.worker.Name(), m.Body))
	if err := c.worker.Do(ctx, m); err != nil {
		log.Warn(fmt.Sprintf("%s处理消息失败: %v", c.worker.Name(), err))
		// 错误分类型发送企业微信
		pm := notify.PostMan{L: notify.SystemErrorWechatNotify{Ctx: ctx, Err: err, Env: conf.Conf.Env.Name}}
		pm.Notify()
		return err
	}
	log.Info(fmt.Sprintf("%s处理消息成功", c.worker.Name()))
	return nil
}

// 消息重新投递
type MessageRetryHandler struct {
	d dao.DataStore
}

func (h MessageRetryHandler) Name() string {
	return "消息重新投递任务"
}

func (h MessageRetryHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.RetryMessage{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	if err := service.NewMessageService(h.d).Emit(ctx, req.ID); err != nil {
		return err
	}
	return nil
}

// 二级商户提现任务
type SubMchWithdrawHandler struct {
	d dao.DataStore
}

func (h SubMchWithdrawHandler) Name() string {
	return "二级商户提现任务"
}

func (h SubMchWithdrawHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.SubMchWithdrawTask{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	if err := service.NewWithdrawService(h.d).ExecuteTask(ctx, req); err != nil {
		return err
	}
	return nil
}

// 二级商户提现查询任务
type SubMchWithdrawQueryHandler struct {
	d dao.DataStore
}

func (h SubMchWithdrawQueryHandler) Name() string {
	return "二级商户提现查询任务"
}

func (h SubMchWithdrawQueryHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.SubMchWithdrawQueryTask{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	if err := service.NewWithdrawService(h.d).ExecuteQueryTask(ctx, req); err != nil {
		return err
	}
	return nil
}

// 批量转账任务
type BatchTransferHandler struct {
	d dao.DataStore
	r *redis.Client
}

func (h BatchTransferHandler) Name() string {
	return "批量转账任务"
}

func (h BatchTransferHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.TransferAwaitAction{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	if err := service.NewTransferService(h.d, h.r).BatchTransfer(ctx, req.OutBatchNo); err != nil {
		return err
	}
	return nil
}

// 进件查询任务
type EcommerceMchApplymentQueryHandler struct {
	d dao.DataStore
}

func (h EcommerceMchApplymentQueryHandler) Name() string {
	return "进件查询任务"
}

func (h EcommerceMchApplymentQueryHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.ApplymentQueryTask{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	if err := service.NewApplymentService(h.d).ExecuteQueryTask(ctx, req); err != nil {
		return err
	}
	return nil
}

// 交易查询任务
type TradeQueryHandler struct {
	d dao.DataStore
}

func (h TradeQueryHandler) Name() string {
	return "交易查询任务"
}

func (h TradeQueryHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.TradeQueryTask{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	if err := service.NewTransService(h.d).SyncTrade(ctx, req.OutTradeNo); err != nil {
		return err
	}
	return nil
}

// 退款查询任务
type RefundQueryHandler struct {
	d dao.DataStore
}

func (h RefundQueryHandler) Name() string {
	return "退款查询任务"
}

func (h RefundQueryHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.RefundQueryTask{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	if err := service.NewRefundService(h.d).RefundSync(ctx, req.OutRefundNo); err != nil {
		return err
	}
	return nil
}

// 分账查询任务
type ProfitOrderQueryHandler struct {
	d dao.DataStore
}

func (h ProfitOrderQueryHandler) Name() string {
	return "分账查询任务"
}

func (h ProfitOrderQueryHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.ProfitOrderQueryTask{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	if err := service.NewProfitService(h.d).ProfitOrderSync(ctx, req.OutOrderNo); err != nil {
		return err
	}
	return nil
}

// 分账回退查询任务
type ProfitReturnQueryHandler struct {
	d dao.DataStore
}

func (h ProfitReturnQueryHandler) Name() string {
	return "分账回退查询任务"
}

func (h ProfitReturnQueryHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.ProfitReturnQueryTask{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	if err := service.NewProfitService(h.d).ProfitReturnSync(ctx, req.OutReturnNo); err != nil {
		return err
	}
	return nil
}

// 批量转账查询任务
type BatchTransferQueryHandler struct {
	d dao.DataStore
	r *redis.Client
}

func (h BatchTransferQueryHandler) Name() string {
	return "批量转账查询任务"
}

func (h BatchTransferQueryHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.TransferQueryTask{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	if err := service.NewTransferService(h.d, h.r).BatchTransferSync(ctx, req.OutBatchNo); err != nil {
		return err
	}
	return nil
}

// 补偿失败明细 - 补偿失败明细的原因
type TransferDetailsCompensateHandler struct {
	d dao.DataStore
	r *redis.Client
}

func (h TransferDetailsCompensateHandler) Name() string {
	return "补偿转账明细任务"
}

func (h TransferDetailsCompensateHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.BatchTransferNotification{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	// 合并orders
	allOrders := append(req.SuccessOrders, req.FailedOrders...)
	// 同步转账详情
	if err := service.NewTransferService(h.d, h.r).TransferDetailsSync(ctx, allOrders, req.OutBatchNo); err != nil {
		return err
	}
	return nil
}

// 进件支付宝当面付签约任务
type SubMchAliFaceToFaceSignQueryHandler struct {
	d dao.DataStore
}

func (h SubMchAliFaceToFaceSignQueryHandler) Name() string {
	return "支付宝当面付签约查询任务"
}

func (h SubMchAliFaceToFaceSignQueryHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.AliFaceToFaceSignQueryTask{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	if err := service.NewAlipayAgentService(h.d).ExecuteFaceToFaceSignQueryTask(ctx, req); err != nil {
		return err
	}
	return nil
}

// 微信特约商户进件查询任务
type SubMchApplymentQueryHandler struct {
	d dao.DataStore
}

func (h SubMchApplymentQueryHandler) Name() string {
	return "微信特约商户进件查询任务"
}

func (h SubMchApplymentQueryHandler) Do(ctx context.Context, m *nsq.Message) (err error) {
	req := &model.SubMchApplymentQueryTask{}
	if err := json.Unmarshal(m.Body, req); err != nil {
		return merrors.E(merrors.Op(h.Name()), merrors.Internal, fmt.Errorf("unmarshal err, %v", err))
	}
	if err := service.NewApplymentService(h.d).ExecuteSubApplymentQuery(ctx, req); err != nil {
		return err
	}
	return nil
}
